Android DecorView 一窥全貌(上)

您所在的位置:网站首页 android activity 线程 Android DecorView 一窥全貌(上)

Android DecorView 一窥全貌(上)

2023-04-21 07:16| 来源: 网络整理| 查看: 265

前言

系列文章:

Android DecorView 一窥全貌(上) Android DecorView 一窥全貌(下)

我们都知道DecorView是最顶层View(根View),它是怎么创建和使用的呢? 通过本篇文章,你将了解到:

1、DecorView创建过程 2、DecorView与Window/Activity关系 3、DecorView各个子View创建

DecorView创建过程

来回顾一下Activity创建过程:

image.png

AMS管理着Activity生命周期,每当切换Activity状态时通过Binder告诉ActivityThread,ActivityThread通过Handler切换到主线程(UI线程)执行,最终分别调用到我们熟知的onCreate(xx)/onResume()方法。 更多细节请移步:Android Activity创建到View的显示过程 本次关注的重点是setContentView(xx)方法。

简单布局分析

相信大家都知道,该方法是将我们布局(layout)文件添加到一个id为**“android.R.id.content”**的ViewGroup里,我们只需要关心layout的内容。那么R.id.content是整个View树的根布局吗?来看看一个最简单的Activity的布局:

my_layout.xml 复制代码

效果图:

image.png

把布局观察器打开:

image.png

可以看出,"content"布局对应的是ContentFrameLayout,它并不是View树的根布局,根布局是DecorView,DecorView和ContentFrameLayout之间还有几个View,接下来我们就来了解上图的这些View是怎么确定的。

setContentView(xx)源码解析

从Activity onCreate(xx)看起

AppCompatDelegateImpl @Override protected void onCreate(Bundle savedInstanceState) { //先调用父类构造函数 //初始化一些必要变量 super.onCreate(savedInstanceState); setContentView(R.layout.layout_path); } 复制代码

创建及初始化AppCompatDelegateImpl

AppCompatActivity.java protected void onCreate(@Nullable Bundle savedInstanceState) { final AppCompatDelegate delegate = getDelegate(); delegate.installViewFactory(); //交给AppCompatDelegate代理 delegate.onCreate(savedInstanceState); super.onCreate(savedInstanceState); } 复制代码

AppCompatActivity将工作交给了AppCompatDelegate处理,而AppCompatDelegate是抽象类,真正工作的是其子类:AppCompatDelegateImpl。

@Override public void onCreate(Bundle savedInstanceState) { ensureWindow(); } private void ensureWindow() { //省略 //mHost为创建此代理的Activity if (mWindow == null && mHost instanceof Activity) { attachToWindow(((Activity) mHost).getWindow()); } } private void attachToWindow(@NonNull Window window) { if (mWindow != null) { throw new IllegalStateException( "AppCompat has already installed itself into the Window"); } //接收各种事件的window callback,实际就是将callback再包了一层 mAppCompatWindowCallback = new AppCompatWindowCallback(callback); window.setCallback(mAppCompatWindowCallback); //省略 //使用activity的window mWindow = window; } 复制代码

上面代码的主要工作是关联AppCompatDelegateImpl和Activity的window变量。

接着来看看setContentView(R.layout.layout_path)本尊 Activity的setContentView(xx)最终调用了AppCompatDelegateImpl里的setContentView(xx):

AppCompatDelegateImpl.java @Override public void setContentView(int resId) { //创建SubDecor,从名字可以猜测一下是DecorView的子View ensureSubDecor(); //找到R.id.content布局 ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content); //清空content里的子View contentParent.removeAllViews(); //加载在activity里设置的layout,并添加到content里 LayoutInflater.from(mContext).inflate(resId, contentParent); } 复制代码

可以看出,我们自定义的layout最后被添加到contentParent里,而contentParent是从mSubDecor里找寻的,因此我们重点来看看ensureSubDecor()方法。 ensureSubDecor里调用的是createSubDecor()方法来创建subDecor。

subDecor创建 AppCompatDelegateImpl.java private ViewGroup createSubDecor() { //根据Activity的主题设置不同设置window的feature TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme); //确保window已经关联 ensureWindow(); //获取DecorView,没有则创建 mWindow.getDecorView(); ViewGroup subDecor = null; //有标题 if (!mWindowNoTitle) { //根据条件给subDecor加载不同的布局文件 if (mIsFloating) { subDecor = (ViewGroup) inflater.inflate( R.layout.abc_dialog_title_material, null); } else if (mHasActionBar) { //加载subDecor布局 subDecor = (ViewGroup) LayoutInflater.from(themedContext) .inflate(R.layout.abc_screen_toolbar, null); //寻找subDecor子布局 mDecorContentParent = (DecorContentParent) subDecor .findViewById(R.id.decor_content_parent); mDecorContentParent.setWindowCallback(getWindowCallback()); } } else { //省略 } //标题栏标题 if (mDecorContentParent == null) { mTitleView = (TextView) subDecor.findViewById(R.id.title); } //寻找subDecor子布局,命名为contentView final ContentFrameLayout contentView = (ContentFrameLayout) subDecor.findViewById( R.id.action_bar_activity_content); //找到window里content布局,实际上找的是DecorView里名为content的布局 final ViewGroup windowContentView = (ViewGroup) mWindow.findViewById(android.R.id.content); if (windowContentView != null) { //挨个移除windowContentView的子View,并将之添加到contentView里 while (windowContentView.getChildCount() > 0) { final View child = windowContentView.getChildAt(0); windowContentView.removeViewAt(0); contentView.addView(child); } //把windowContentView id去掉,之前名为content windowContentView.setId(View.NO_ID); //将"content"名赋予contentView contentView.setId(android.R.id.content); } //把subDecor添加为Window的contentView,实际上添加为DecorView的子View。该方法后面再具体分析 mWindow.setContentView(subDecor); return subDecor; } 复制代码

该方法较长,省略了一些细枝末节,主体功能是:

根据不同的前置条件,加载不同的布局文件作为subDecor 将subDecor加入到DecorView里,subDecor本身是ViewGroup

subDecor创建了,那么DecorView啥时候创建呢?createSubDecor()有段代码:

mWindow.getDecorView() 复制代码 DecorView创建

mWindow之前分析过是Activity的window变量,它的实现类是PhoneWindow。

PhoneWindow.java View getDecorView() { if (mDecor == null || mForceDecorInstall) { installDecor(); } return mDecor; } private void installDecor() { //mDecor作为PhoneWindow变量 if (mDecor == null) { //新建DecorView,并关联window mDecor = generateDecor(-1); } if (mContentParent == null) { //创建DecorView子布局 mContentParent = generateLayout(mDecor); } } protected DecorView generateDecor(int featureId) { Context context; if (mUseDecorContext) { Context applicationContext = getContext().getApplicationContext(); if (applicationContext == null) { context = getContext(); } else { //applicationContext是Application //getContext 是Activity //DecorContext 继承自ContextThemeWrapper context = new DecorContext(applicationContext, getContext()); if (mTheme != -1) { context.setTheme(mTheme); } } } else { context = getContext(); } //this 指的是phoneWindow,赋值给mWindow变量 return new DecorView(context, featureId, this, getAttributes()); } 复制代码

从上可知:

DecorView继承自FrameLayout

注:DecorContext 有关请移步:Android各种Context的前世今生

重点来看看

mContentParent = generateLayout(mDecor);

PhoneWindow.java protected ViewGroup generateLayout(DecorView decor) { //获取WindowStyle TypedArray a = getWindowStyle(); //根据style设置各种flags和feature //省略... WindowManager.LayoutParams params = getAttributes(); //待加载的布局资源id int layoutResource; int features = getLocalFeatures(); //根据不同的feature确定不同的布局 if ((features & (1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3